package com.dycong.common.database.hibernate3.OMS;

import com.dycong.common.database.mongoDB.JaversUtil;
import com.dycong.common.reflect.ClassUtil;
import lombok.extern.log4j.Log4j;
import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.javers.core.Javers;
import org.javers.spring.annotation.JaversAuditable;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by yu on 15-9-9.
 * todo OMS里从mysql简单读取数据的工具类使用了org.hibernate.criterion类似与mybatis的一中操作
 */
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Log4j
public class RdbBaseService {

    @Resource
    BaseDao baseDao;

    @Resource
    Javers javers;

    @Transactional
    public Serializable save(Object entity){
        return baseDao.save(entity);
    }

    @Transactional
    public <T> Collection<T> save(Collection<T> entities){
        entities.forEach(s->{save(s);});
        return entities;
    }

    @JaversAuditable
    @Transactional
    public void saveOrUpdate(Object entity){
        baseDao.saveOrUpdate(entity);
    }

    public <T> T get(Class<T> clazz, Serializable id){
        T object= baseDao.get(clazz, id);
        try {
            if (object!=null && !object.getClass().getTypeName().contains("jvst")){
                javers.commit(JaversUtil.user,object);
            }
        }catch (Exception e){
            log.error(e);
        }
        return object;
    }

    public <T> List<T> getList(Class<T> clazz) {
        return baseDao.getList(clazz);
    }


    public <T> List<T> getListByField(Class<T> clazz, String fieldName, Object value) {
        return baseDao.getListByField(clazz, fieldName, value,null,null);
    }
    public <T> List<T> getListByField(Class<T> clazz, String fieldName1, Object value1, String fieldName2, Object value2) {
        return baseDao.getListByField(clazz, fieldName1, value1,fieldName2,value2);
    }
    public <T> T getOneByField(Class<T> clazz, String fieldName, Object value){
        return baseDao.getOneByField(clazz, fieldName, value);
    }


    public <T> List<T> fetch(Class<T> clazz, Map<String, Object> filterMap, String sortField, boolean isASC, int start, int size) {
        Criteria criteria = baseDao.createCriteria(clazz);
        baseDao.addFiltersToCriteria(filterMap, criteria, clazz);


        if (sortField != null && !sortField.isEmpty()) {
            log.debug("add sort field: " + sortField + ", ORDER: " + (isASC ? "ASC" : "DESC"));
            Order order = isASC ? Order.asc(sortField) : Order.desc(sortField);
            criteria.addOrder(order);
        }else {
            criteria.addOrder(Order.desc("id"));
        }
        if (start < 0) start = 0;
        if (size < 0) size = 50;
        return criteria
                .setFirstResult(start)
                .setMaxResults(size)
                .setCacheable(true)
                .list();
    }

    public <T> long count(Class<T> clazz, Map<String, Object> filterMap) {
        Criteria criteria = baseDao.createCriteria(clazz);
        baseDao.addFiltersToCriteria(filterMap, criteria, clazz);
        Object o=criteria
                .setProjection(Projections.rowCount())
                .setCacheable(true)
                .uniqueResult();
        if (o!=null){
            return (long) o;
        }
        return 0;
    }
    @JaversAuditable
    @Transactional
    public void delete(Object entity){
         baseDao.delete(entity);
    }

    @Transactional
    public void deleteCollection(Collection<?> entities){
        entities.forEach(s->{delete(s);});
    }

    public <T> List<T> fetch(Class<T> clazz, Map<String, Object> filterMap, Map<String, Object> greatOrEqual, Map<String, Object> lessThan, String sortField, boolean isASC, int start, int size) {
        Criteria criteria = baseDao.createCriteria(clazz);
        baseDao.addFiltersToCriteria(filterMap, criteria, clazz);

        if (greatOrEqual != null) {
            greatOrEqual.forEach((k, v) -> {
                if (k != null && !k.isEmpty())
                    log.debug("got field: " + k + " -> " + v);
                ClassUtil.getFieldType(clazz, k).ifPresent(type -> {
                    if (ClassUtil.isNumberType(type) || ClassUtil.isEnumType(type)) {
                        ClassUtil.newInstance(type, v).ifPresent(fieldValue -> {
                            log.debug("add restriction ge: " + k + " -> " + v);
                            criteria.add(Restrictions.ge(k, fieldValue));
                        });
                    } else if (type == String.class) {
                        log.debug("add restriction LIKE: " + k + " -> " + v);
                        criteria.add(Restrictions.ge(k, v.toString()));
                    }else if(type == Date.class && v !=null){
                        log.debug("add date ge: " + k + " -> " + v);
                        criteria.add(Restrictions.ge(k, v));
                    }
                });
            });
        }
        if (lessThan != null) {
            lessThan.forEach((k, v) -> {
                if (k != null && !k.isEmpty())
                    log.debug("got field: " + k + " -> " + v);
                ClassUtil.getFieldType(clazz, k).ifPresent(type -> {
                    if (ClassUtil.isNumberType(type) || ClassUtil.isEnumType(type)) {
                        ClassUtil.newInstance(type, v).ifPresent(fieldValue -> {
                            log.debug("add restriction lt: " + k + " -> " + v);
                            criteria.add(Restrictions.le(k, fieldValue));
                        });
                    } else if (type == String.class) {
                        log.debug("add restriction LIKE: " + k + " -> " + v);
                        criteria.add(Restrictions.le(k, v.toString()));
                    }else if(type == Date.class && v !=null){
                        log.debug("add date ge: " + k + " -> " + v);
                        criteria.add(Restrictions.le(k, v));
                    }
                });
            });
        }

        if (sortField != null && !sortField.isEmpty()) {
            log.debug("add sort field: " + sortField + ", ORDER: " + (isASC ? "ASC" : "DESC"));
            Order order = isASC ? Order.asc(sortField) : Order.desc(sortField);
            criteria.addOrder(order);
        }
        if (start < 0) start = 0;
        if (size < 0) size = 50;
        return criteria
                .setFirstResult(start)
                .setMaxResults(size)
                .setCacheable(true)
                .list();
    }

    public <T> List<T> fetch(Class<T> clazz, Map<String, Object> equal, boolean isASC, int start, int size) {
        Criteria criteria = baseDao.createCriteria(clazz);

        if (equal != null) {
            equal.forEach((k, v) -> {
                if (k != null && !k.isEmpty())
                    log.debug("got field: " + k + " -> " + v);
                ClassUtil.getFieldType(clazz, k).ifPresent(type -> {
                    if (ClassUtil.isNumberType(type) || ClassUtil.isEnumType(type)) {
                        ClassUtil.newInstance(type, v).ifPresent(fieldValue -> {
                            log.debug("add restriction eq: " + k + " -> " + v);
                            criteria.add(Restrictions.eq(k, fieldValue));
                        });
                    } else if (type == String.class) {
                        log.debug("add restriction LIKE: " + k + " -> " + v);
                        criteria.add(Restrictions.eq(k, v.toString()));
                    }
                });
            });
        }

        if (start < 0) start = 0;
        if (size < 0) size = 50;
        return criteria
                .setFirstResult(start)
                .setMaxResults(size)
          //      .setCacheable(true)
                .list();
    }

    public <T> long count(Class<T> clazz, Map<String, Object> filters, Map<String, Object> geFilters, Map<String, Object> ltFilters) {
        Criteria criteria = baseDao.createCriteria(clazz);
        baseDao.addFiltersToCriteria(filters, criteria, clazz);

        if (geFilters != null) {
            geFilters.forEach((k, v) -> {
                if (k != null && !k.isEmpty())
                    log.debug("got field: " + k + " -> " + v);
                ClassUtil.getFieldType(clazz, k).ifPresent(type -> {
                    if (ClassUtil.isNumberType(type) || ClassUtil.isEnumType(type)) {
                        ClassUtil.newInstance(type, v).ifPresent(fieldValue -> {
                            log.debug("add restriction ge: " + k + " -> " + v);
                            criteria.add(Restrictions.ge(k, fieldValue));
                        });
                    } else if (type == String.class) {
                        log.debug("add restriction LIKE: " + k + " -> " + v);
                        criteria.add(Restrictions.ge(k, v.toString()));
                    }else if(type == Date.class && v !=null){
                        log.debug("add date ge: " + k + " -> " + v);
                        criteria.add(Restrictions.ge(k, v));
                    }
                });
            });
        }
        if (ltFilters != null) {
            ltFilters.forEach((k, v) -> {
                if (k != null && !k.isEmpty())
                    log.debug("got field: " + k + " -> " + v);
                ClassUtil.getFieldType(clazz, k).ifPresent(type -> {
                    if (ClassUtil.isNumberType(type) || ClassUtil.isEnumType(type)) {
                        ClassUtil.newInstance(type, v).ifPresent(fieldValue -> {
                            log.debug("add restriction lt: " + k + " -> " + v);
                            criteria.add(Restrictions.le(k, fieldValue));
                        });
                    } else if (type == String.class) {
                        log.debug("add restriction LIKE: " + k + " -> " + v);
                        criteria.add(Restrictions.le(k, v.toString()));
                    }else if(type == Date.class && v !=null){
                        log.debug("add date ge: " + k + " -> " + v);
                        criteria.add(Restrictions.le(k, v));
                    }
                });
            });
        }
        return (long) criteria
                .setProjection(Projections.rowCount())
                .setCacheable(true)
                .uniqueResult();
    }
}
